package com.lmy.androidcustomview.touch

import android.animation.ObjectAnimator
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Paint
import android.os.Build
import android.util.AttributeSet
import android.util.Log
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.View
import android.widget.OverScroller
import androidx.annotation.RequiresApi
import androidx.core.view.GestureDetectorCompat
import androidx.core.view.ScaleGestureDetectorCompat
import androidx.core.view.ViewCompat.postOnAnimation
import com.lmy.androidcustomview.R
import com.lmy.androidcustomview.dp
import java.lang.Float.max
import java.lang.Float.min

/**
 * @author
 * @功能:
 * @Creat 2/13/21 1:19 PM
 * @Compony 465008238@qq.com
 */

class ScalableImageView(context: Context, attrs: AttributeSet) : View(context, attrs), Runnable {
    private val IMAGE_SIZE = 300.dp
    private val bitmap = getAvatar(IMAGE_SIZE.toInt())
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    //图片的xy坐标原始的偏移量
    private var originaloffsetx = 0f
    private var originaloffsety = 0f

    //手指滑动以后图片的xy坐标的额外偏移量
    private var offsetx = 0f
    private var offsety = 0f

    //图片横向贴边和纵向贴边的缩放比例
    private var smallScale = 0f
    private var bigScale = 0f

    //为了做滑动 多出来的放缩比例
    private val EXTRA_SCALE_FRACTOR = 1.5f


    //用于判断属性动画正序还是倒序
    private var big = false

    //绘制的缩放比例 影响绘制
    private var currentScale = 0f
        set(value) {
            field = value
            invalidate()
        }

    /**
     * 定义属性动画
     */
    private val scaleAnimator = ObjectAnimator.ofFloat(this, "currentScale", smallScale, bigScale)

    /**
     * 计算坐标使用的 惯性滑动的计算
     */
    private val scroller = OverScroller(context)


    /**
     * 自定义的GestureDetector.SimpleOnGestureListener()监听器
     */
    private val customGestureListenner = CustomGestureListenner()

    /**
     * gestureDetector监听工具类
     * 使用提供的MotionEvent检测各种手势和事件。 当发生特定的运动事件时，
     */
    private val gestureDetector = GestureDetectorCompat(context, customGestureListenner)


    /**
     * 自定义的ScaleGestureDetector.OnScaleGestureListener()监听器
     */
    private val customscaleGestureListenner = CustomscaleGestureListenner()

    /**
     *测缩放转换手势工具类
     */
    private val scaleGestureDetector = ScaleGestureDetector(context, customscaleGestureListenner)

    /**
     * xy轴边界偏移量的修正
     */
    @RequiresApi(Build.VERSION_CODES.N)
    private fun fixOffsetXY() {
        offsetx = min(offsetx, (bitmap.width * bigScale - width) / 2)
        offsetx = max(offsetx, -(bitmap.width * bigScale - width) / 2)
        offsety = min(offsety, (bitmap.height * bigScale - height) / 2)
        offsety = max(offsety, -(bitmap.height * bigScale - height) / 2)
    }

    override fun run() {
        //返回值代表快滑是否结束
        if (scroller.computeScrollOffset()) {
            offsetx = scroller.currX.toFloat()
            offsety = scroller.currY.toFloat()
            invalidate()
            postOnAnimation(this)
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        //属性动画的缩放比例
        var scaleFraction = (currentScale - smallScale) / (bigScale - smallScale)
        canvas.translate(offsetx * scaleFraction, offsety * scaleFraction)
        //初始值+差值*scaleFraction(动画进度百分比)
        //设置缩放比例和缩放的轴心 轴心为View的中心
        canvas.scale(currentScale, currentScale, width / 2f, height / 2f)
        //绘制Bitmap
        canvas.drawBitmap(bitmap, originaloffsetx, originaloffsety, paint)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        //图片居中
        originaloffsetx = (width - IMAGE_SIZE) / 2f
        originaloffsety = (height - IMAGE_SIZE) / 2f
        //判断图片是个比较宽的还是比较窄的
        if (bitmap.width / bitmap.height.toFloat() > width / height.toFloat()) {
            //图片是个比较宽的
            smallScale = width / bitmap.width.toFloat()
            bigScale = height / bitmap.height.toFloat() * EXTRA_SCALE_FRACTOR
        } else {
            //图片比较窄
            smallScale = height / bitmap.height.toFloat()
            bigScale = width / bitmap.width.toFloat() * EXTRA_SCALE_FRACTOR
        }
        currentScale = smallScale
        scaleAnimator.setFloatValues(smallScale, bigScale)
    }


    /**
     * 限制图片的大小
     */
    private fun getAvatar(width: Int): Bitmap {
        val options = BitmapFactory.Options()
        options.inJustDecodeBounds = true
        BitmapFactory.decodeResource(
            resources,
            R.drawable.ic_head, options
        )
        options.inJustDecodeBounds = false
        options.inDensity = options.outWidth
        options.inTargetDensity = width
        return BitmapFactory.decodeResource(
            resources,
            R.drawable.ic_head, options
        )
    }

    //废弃原生的onTouchEvent方法 设置为gestureDetector的onTouchEvent方法
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        scaleGestureDetector.onTouchEvent(event)
        //当进入双指缩放 就不再工作gestureDetector了
        if (!scaleGestureDetector.isInProgress) {
            gestureDetector.onTouchEvent(event)
        }
        return true
    }

    inner class CustomGestureListenner : GestureDetector.SimpleOnGestureListener() {
        /**
         * 是否消费事件 这个一般是永远返回ture的
         */
        override fun onDown(e: MotionEvent?): Boolean {
            return true
        }

        /**
         * 快滑的时候被调用 手指松掉以后惯性的滑动
         *
         * 开始的down事件对象
         * 这次的发生的事件对象
         * velocityX  velocityY横向和纵向方向滑动的速率
         *
         * 返回值 是否消费事件 返回值没用 因为事件的消费是从onDown开始消费的
         */
        override fun onFling(
            downEvent: MotionEvent?,
            currentEvent: MotionEvent?,
            velocityX: Float,
            velocityY: Float
        ): Boolean {
            /**
             * 大图下快滑
             */
            if (big) {
                /**
                 * 起始坐标
                 * 横向纵向速度
                 * 最小最大坐标
                 * overx y额外滑动的距离 滑动以后会返回最终位置
                 */
                scroller.fling(
                    offsetx.toInt(), offsety.toInt(), velocityX.toInt(), velocityY.toInt(),
                    (-(bitmap.width * bigScale - width) / 2).toInt(),
                    ((bitmap.width * bigScale - width) / 2).toInt(),
                    (-(bitmap.height * bigScale - height) / 2).toInt(),
                    ((bitmap.height * bigScale - height) / 2).toInt(),
                    40.dp.toInt(),
                    40.dp.toInt()
                )
                //这样写比较节省资源
                postOnAnimation(this@ScalableImageView, this@ScalableImageView)
            }
            return false
        }

        /**
         * 当手指发生移动的时候就会被触发
         *
         * 参数
         * 开始的down事件对象
         * 这次的发生的事件对象
         * 这次事件和上次事件两个点之间的距离 x y
         * 旧的位置-新的位置的值
         *
         * 返回值 是否消费事件 返回值没用 因为事件的消费是从onDown开始消费的
         */
        @RequiresApi(Build.VERSION_CODES.N)
        override fun onScroll(
            downEvent: MotionEvent?,
            currentEvent: MotionEvent?,
            distanceX: Float,
            distanceY: Float
        ): Boolean {
            //只有放大状态下才可以移动
            if (big) {
                /**
                 * 这里需要计算下放大以后 图片和View之间的横向纵向距离
                 * 然后修正最大滑动距离
                 */
                offsetx -= distanceX
                offsety -= distanceY
                fixOffsetXY()
                invalidate()
            }
            return false
        }

        /**
         * 连续按下两次会触发
         * 有一个很短的时间间隔300ms
         * 如果断于40ms 就不会触发
         * 返回值没用
         */
        @RequiresApi(Build.VERSION_CODES.N)
        override fun onDoubleTap(e: MotionEvent): Boolean {
            big = !big
            //动画正序和倒序
            if (big) {
                //处理放大跟手 并且修正边缘值
                offsetx = (e.x - width / 2f) * (1 - bigScale / smallScale)
                offsety = (e.y - height / 2f) * (1 - bigScale / smallScale)
                fixOffsetXY()
                scaleAnimator.start()
            } else {
                scaleAnimator.reverse()
            }
            return false
        }
    }

    inner class CustomscaleGestureListenner : ScaleGestureDetector.OnScaleGestureListener {
        /**
         * 开始缩放
         * 必须返回true拦截事件
         */
        override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
            //处理跟手缩放 放大
            offsetx = (detector.focusX - width / 2f) * (1 - bigScale / smallScale)
            offsety = (detector.focusY - height / 2f) * (1 - bigScale / smallScale)
            return true
        }

        /**
         * 结束缩放
         */
        override fun onScaleEnd(detector: ScaleGestureDetector?) {
        }

        /**
         * 过程中
         * 返回false 则detector.scaleFactor就是初始状态的放缩系数
         * true 则detector.scaleFactor就是上一次缩放状态的放缩系数
         */
        @RequiresApi(Build.VERSION_CODES.N)
        override fun onScale(detector: ScaleGestureDetector): Boolean {
            /**
             * 这里以默认的放缩比例作为基准然后*放缩的系数得到真正的 view缩放比例
             */
            val tempCurrentScale = currentScale * detector.scaleFactor
            if (tempCurrentScale < smallScale || tempCurrentScale > bigScale) {
                return false
            } else {
                currentScale *= detector.scaleFactor
                //实时放缩系数 当前状态和初始状态的放缩系数
                //解决缩放边界的问题
                currentScale = max(currentScale, smallScale)
                currentScale = min(currentScale, bigScale)
                //如果当前缩放比例大于最小的smallScale则表示当前View进行了放大则将big标记设置为true
                if (currentScale>smallScale){
                    big=true
                }
                return true
            }
        }
    }
}